﻿
using System;
using System.Collections.Generic;
using UnityEngine;

namespace xres
{


    public class BundleLoader : BaseLoader
    {
        public const string Err_Load_Bundle = "Err_Load_Bundle";

        internal readonly string _bundleName;
        //public string BundleName { get { return _bundleName; } }

        //LoadMode _loadMode = LoadMode.Init;

        AsyncOperation _op;

        /// <summary>
        /// 我的依赖
        /// </summary>
        internal readonly List<BundleLoader> _myDepList = new List<BundleLoader>();

        /// <summary>
        /// 依赖我的
        /// </summary>
        internal readonly List<BundleLoader> _depMeList = new List<BundleLoader>();

        internal readonly List<BundleAssetLoader> _assetLoaderList = new List<BundleAssetLoader>();

        /// <summary>
        /// 最终加载到的资源
        /// </summary>
        public AssetBundle bundle { get; internal set; }


        public BundleLoader(string bundleName)
        {
            _bundleName = bundleName;
        }

        //==================== Unload

        bool CanUnload(bool callInUnload)
        {
            if (LoaderState.Finish != _state && LoaderState.Init != _state) //todo: 正在加载中允许释放吗?
            {
                if (callInUnload) Debug.Log($"{_bundleName}: not load finish, not unload");
                return false;
            }

            //===== 自己是否可释放
            var ct = GetRefCount();
            if (ct > 0)
            {
                if (callInUnload) Debug.Log($"{_bundleName}: has ref pages: {ct}, not unload");
                return false;
            }

            //资源是否被引用着
            for (var i = 0; i < _assetLoaderList.Count; ++i)
            {
                var assetLoader = _assetLoaderList[i];
                if (assetLoader.GetRefCount() > 0)
                {
                    if (callInUnload) Debug.Log($"{_bundleName}: {assetLoader._assetName}, ref>0: {ct}, not unload");
                    return false;
                }
            }

            //===== 依赖我的是否可释放
            for (var i = 0; i < _depMeList.Count; ++i)
            {
                var depMe = _depMeList[i];
                if (!depMe.CanUnload(false))
                {
                    Debug.Log($"{_bundleName}: depBy {depMe._bundleName}, not unload");
                    return false;
                }
            }

            return true;
        }

        public void Unload()
        {
            if (!CanUnload(true))
                return;

            _DoUnload(true);
        }

        internal void _DoUnload(bool callInUnload)
        {
            if (callInUnload) Debug.Log($"{_bundleName}: unload");

            if (_state == LoaderState.Init) //已释放
                return;

            if (null != bundle)
            {
                //使用xRes加载的资源释放(还有一些在prefab上的被加载的资源不会出现在这里)
                for (var i = 0; i < _assetLoaderList.Count; ++i)
                {
                    _assetLoaderList[i]._DoUnload();
                }

                bundle.Unload(true);
                bundle = null;
            }
            _state = LoaderState.Init;

            if (false) //todo: 暂时屏蔽: 依赖我的要由我释放吗? 
            {
                for (var i = 0; i < _depMeList.Count; ++i)
                {
                    var depMe = _depMeList[i];
                    depMe._DoUnload(false);
                    Debug.Log($"{_bundleName}: unload, depBy {depMe._bundleName}");
                }
            }
        }

        //====================

        public override bool IsLoadFinish()
        {
            if (LoaderState.Finish != _state)
                return false;

            return IsAllDepBundlesLoadFinish();
        }

        /// <summary>
        /// 整个加载进度(包括依赖资源)
        /// </summary>
        /// <returns></returns>
        public float CheckProgress()
        {
            switch (_state)
            {
            case LoaderState.Init:
                return 0;

            case LoaderState.LoadAssetBundle:
            {
                var count = _myDepList.Count;
                if (count <= 0)
                    return _op.progress;

                var progress = _op.progress;
                for (int i = 0; i < count; ++i)
                {
                    var dep = _myDepList[i];
                    progress += dep.BundleProgress();
                }

                return progress / (count + 1.0f);
            }

            }

            return 1;
        }

        /// <summary>
        /// 自己的加载进度
        /// </summary>
        /// <returns></returns>
        public float BundleProgress()
        {
            switch (_state)
            {
            case LoaderState.Init:
                return 0;

            case LoaderState.LoadAssetBundle:
                return _op.progress;

            }

            return 1;
        }

        public void Load(bool async)
        {
            if (LoaderState.Finish == _state && string.IsNullOrEmpty(ErrType)) //成功加载后就不再加载
                return;

            switch (_state)
            {
            case LoaderState.Init:
            case LoaderState.Finish:
                var mode = async ? LoadMode.Async : LoadMode.Sync;
                //_loadMode = mode;
                if (async)
                    LoadAsync();
                else
                    LoadSync();
                return;
            }

            //加载中切换加载模式无效
        }

        void LoadSync()
        {
            for (var i = 0; i < _myDepList.Count; ++i)
            {
                var dep = _myDepList[i];

                if (LoaderState.Init == dep._state)
                    dep.Load(false);
                else if (LoaderState.Finish != dep._state)
                    throw new Exception($"{_bundleName}: dep bundle: {dep} load in async mode, and not load finish");
            }

            _state = LoaderState.LoadAssetBundle;
            var path = ResPath.BuildExternalBundlePath(_bundleName);
            var bundle = AssetBundle.LoadFromFile(path);
            if (null != bundle)
            {
                //todo: 要判断吗?
            }

            this.bundle = bundle;
            SetError("", "");
            SetFinishAndNotify("");
        }

        void LoadAsync()
        {
            for (var i = 0; i < _myDepList.Count; ++i)
            {
                var dep = _myDepList[i];
                if (dep.IsLoadFinish()) //todo: 不管有没加载成功, IsLoadSuccess()?
                    continue;

                dep.onComplete.AddListener(OnComplete_Dep);
                dep.Load(true); //还没开始或还在加载中
            }

            _state = LoaderState.LoadAssetBundle;
            var path = ResPath.BuildExternalBundlePath(_bundleName);
            //Debug.Log($"BundleLoader: Load: bundlePath: {path}");
            var request = AssetBundle.LoadFromFileAsync(path);
            _op = request;
            request.completed += OnComplete_Bundle;
        }

        void OnComplete_Bundle(AsyncOperation op)
        {
            _op = null;

            var request = (AssetBundleCreateRequest)op;
            if (null == request.assetBundle)
            {
                SetError(Err_Load_Bundle, $"bundle load fail");
                Debug.LogError(ErrMsg);
                //bundle = null;
                _state = LoaderState.Finish;
                NotifyComplete(""); //自身加载出错了, 不要等deps了, 直接回调
                return;
            }

            //Debug.Log($"bundle load ok");
            bundle = request.assetBundle;
            SetError("", "");
            _state = LoaderState.Finish; //自身加载完毕

            //依赖Bundle是否都已加载完毕
            if (IsAllDepBundlesLoadFinish())
            {
                NotifyComplete("");
            }
        }

        bool IsAllDepBundlesLoadFinish()
        {
            for (var i = 0; i < _myDepList.Count; ++i)
            {
                if (!_myDepList[i].IsLoadFinish())
                    return false;
            }

            return true;
        }

        void OnComplete_Dep(object obj, string type, object data)
        {
            if (LoaderState.Finish == _state
                && !string.IsNullOrEmpty(ErrMsg)
                ) 
            {
                //如果自身已经加载出错了, 在出错时就已经通知过了
                return;
            }

            var loader = (BundleLoader)obj;
            Debug.Log($"BundleLoader: OnComplete_Dep: bundle: {loader._bundleName}");

            if (IsLoadFinish()) //所有包加载完毕
            {
                NotifyComplete("");
            }
        }

    } //end of class

}
